<?php
/*****************************************
 This file is part of the Dynamo CMS UI Toolkit
 Copyright (c) Dynamo Studios 2008

 *** NOTE ***
 Permission is hereby granted to execute this file as a
 web service under the terms of the license agreement.

 See LICENSE.txt, in the system folder.

 All other rights, other than those expressed in the license
 document, are reserved.

 ****************************************/

/**
 * @name Page Library
 * @version 0.1 (Alvin)
 * @abstract
 * Provides widgets for navigation menus, displaying formatted
 * text, and linking between objects.
 * 
 * @todo Escaping of special characters.
 *
 * @author Sean Micklethwaite
 */


class cUiWikiRenderer extends cDycmsClientNode
{
	function __construct($zTokens, $data, $zAllowedHtmlTags,
						 $argv = array(), $cmdProc = 'cUiWikiCmdProcessor'){
		$cnode = $this;
		$listDepth = 0;
		$current = strtok($data, $zTokens['LineBreak']);
		$cnodeType = '';
		while($current !== false){
		 	$tbuf = '';
		 	$len = strlen($current);
		 	$i = 0;
		 	
		 	/* Check for things we expect at the start of a line only */
		 	
		 	switch($current[0]){
		 		
				case $zTokens['Heading']:
					
				// Close lists
				while($listDepth > 0){
			 		$cnode = $cnode->GetParent();
			 		$listDepth--;
			 	};
				
				// Get heading depth
				$i = 1;
				while(($i < strlen($current)) && ($current[$i] == $zTokens['Heading'])){
					 $i++;
				};
	
				// Create the heading
				$cnode->AddChild($cnode = new cUiHeading(null, $i));
				
				// Process the line
				$len -= $i;
				$cnodeType = 'Heading';
				break;
				
				
			 	case $zTokens['BulletPoint']:
						
				// Output buffered text
				$cnode->AddChild(strip_tags($tbuf, $zAllowedHtmlTags));
				
				// Get bullet depth
				for($depth=1, ++$i;
					($i < $len) && ($current[$i] == $zTokens['BulletPoint']); $i++){
					 $depth++;
				};
				
				// Reset the list node, if there is no node or we have left the list
				if($cnodeType != 'BulletPoint'){
				 	$rDepth = 0;
				}else $rDepth = $depth;
				
				// Get to the correct depth
				if($rDepth < $listDepth){
				 	while($rDepth < $listDepth){
				 		$cnode = $cnode->GetParent();
				 		$listDepth--;
				 	};
				};
				if($listDepth < $depth){
				 	while($listDepth < $depth){
				 	 	$cnode->AddChild($cnode = new cHtmlWrapperTag('ul'));
				 	 	$listDepth++;
				 	};
				};
				
				$cnode->AddChild($cnode = new cHtmlWrapperTag('li'));
				
				$cnodeType = 'BulletPoint';
				$i = $depth;
				break;
				
			
				case $zTokens['NumberedPoint']:
				
				// Output buffered text
				$cnode->AddChild(strip_tags($tbuf, $zAllowedHtmlTags));
				
				// Get bullet depth
				for($depth=1, ++$i;
					($i < $len) && ($current[$i] == $zTokens['NumberedPoint']); $i++){
					 $depth++;
				};
				
				// Reset the list node, if there is no node or we have left the list
				if($cnodeType != 'NumberedPoint'){
				 	$rDepth = 0;
				}else $rDepth = $depth;
				
				// Get to the correct depth
				if($rDepth < $listDepth){
				 	while($depth < $listDepth){
				 		$cnode = $cnode->GetParent();
				 		$listDepth--;
				 	};
				};
				if($listDepth < $depth){
				 	while($listDepth < $depth){
				 	 	$cnode->AddChild($tmp = new cHtmlWrapperTag('ol'));
				 	 	$cnode = $tmp;
				 	 	$listDepth++;
				 	};
				};
				
				$cnode->AddChild($cnode = new cHtmlWrapperTag('li'));
				
				$cnodeType = 'NumberedPoint';
				$i = $depth;
				break;
				
				
				default: // Paragraph
				
				// Close lists
				while($listDepth > 0){
			 		$cnode = $cnode->GetParent();
			 		$listDepth--;
			 	};
			 	
				if($argv['noParagraph']){
					$cnodeType = 'None';
				}else{
				 	$cnode->AddChild($cnode = new cHtmlWrapperTag('p'));
				 	$cnodeType = 'Paragraph';
				};
		 	};
		 	
		 	
		 	/* Now parse into the line */
		 	
			for(; $i < $len; $i++){
				switch($current[$i]){
				 
					case $zTokens['WikiLink']:
					
					// Output buffered text
					$cnode->AddChild($argv['allowAllHtml'] ? $tbuf : strip_tags($tbuf, $zAllowedHtmlTags));
					$tbuf = '';
					
					// Check next char matches
					if($current[$i+1] == $zTokens['WikiLink']){
						// Find closing tag
						$close = strpos($current,
							str_repeat($zTokens['WikiLinkEnd'],2), $i+2);
						$text = trim(substr($current, $i+2, $close - ($i+2)));
						$cnode->AddChild(new cUiWikiLink($text, null, $argv));
					};
					
					// Skip to end of link
					$i = $close + 1;
					$lastTok = 'WikiLink';
					break;
					
					case $zTokens['WikiCmd']:
					
					// Output buffered text
					$cnode->AddChild($argv['allowAllHtml'] ? $tbuf : strip_tags($tbuf, $zAllowedHtmlTags));
					$tbuf = '';
					
					// Check next char matches
					if($current[$i+1] == $zTokens['WikiCmd']){
						// Find closing tag
						$close = strpos($current,
							str_repeat($zTokens['WikiCmdEnd'],2), $i+2);
						$text = trim(substr($current, $i+2, $close - ($i+2)));
						$cnode->AddChild(new ${cmdProc}($text, $argv));
					};
					
					// Skip to end of link
					$i = $close + 1;
					$lastTok = 'WikiCmd';
					break;
					
					default:
					// Plain text
					$tbuf .= $current[$i];
					$lastTok = 'PlainText';
				} // switch
			} // for
			
			// Flush text buffer
			$cnode->AddChild($argv['allowAllHtml'] ? $tbuf : strip_tags($tbuf, $zAllowedHtmlTags));
			if($cnodeType != 'None')
				$cnode = $cnode->GetParent();
			
			// Get next line
			$current = strtok($zTokens['LineBreak']);
		}; // while
	}
	
	public static $zDefaultToks = array(
		'Whitespace' => " \n\t\r",
		'LineBreak' => "\n\r",
		'Heading' => '=',
		'WikiLink' => '[',
		'WikiLinkEnd' => ']',
		'WikiCmd' => '{',
		'WikiCmdEnd' => '}',
		'BulletPoint' => '*',
		'NumberedPoint' => '%',
		'TextEm' => "'"
	);
}

class cUiWikiCmdProcessor extends cDycmsClientNode
{
	function __construct($args, $wikiArgv){
		$argv = null;
		$cmd = self::ProcessArgs($args, $argv);
		$this->${cmd}($argv);
	}
	
	static function ProcessArgs($args, &$argv){
		$z = split('->', $args);
				
		// First is command name
		$cmd = $z[0];
		
		if(count($z) == 1){
		 	// No arguments
		 	$argv = null;
		}else{
		 	
			// Now split args by '='
			for($n = 1; $n < count($z); $n++)
				$z[$n] = split('=', $z[$n]);
			
			if(count($z[1]) == 1){
			 	// Just one string argument
			 	$argv = $z[1][0];
			}else{
			 	// Multiple arguments
				$argv = array();
			 	for($n = 1; $n < count($z); $n++){
			 		if(count($z[$n]) == 2)
			 			$argv[$z[$n][0]] = $z[$n][1];
				}
			}
		}
		
		return $cmd;
	}
	
	function icon($argv){
		if(is_string($argv)){
			$this->AddChild(new cUiIcon($argv));
		}
	}
	
	function module($argv){
		if(is_string($argv)) {
			$q = new cDycmsObjectQuery('Module');
			$q->AddArray(array('classname' => $argv));
			if($q->Execute() > 0) {
				$this->AddChild(uiLoadModule($q->Get('classname'), $q->Get('lib')));
			} else {
				$this->AddChild(uiGetIcon('error'));
			}
		}
	}
	
	function addModule($argv) {
		if(is_string($argv)) {
			$q = new cDycmsObjectQuery('Module');
			$q->AddArray(array('classname' => $argv));
			if($q->Execute() > 0) {
				cUiPage::GetInst()->AddModule(uiLoadModule($q->Get('classname'), $q->Get('lib')));
			} else {
				$this->AddChild(uiGetIcon('error'));
			}
		}
	}
	
	function drop($argv) {
		$zone = cUiPage::GetInst()->GetDropZone($argv['zone']);
		if($zone) {
			$zone->AddChild($argv['html']);
		}
	}
	
	function view($argv) {
		$this->AddChild(new cHtmlHyperlink(dyViewerUrl($argv['clsid'], $argv['id']), $argv['title'], array(
			'class' => 'wikiLink'
		)));
	}
	
	function __call($s, $argv){
		dyLog('Warning: unknown wiki command - '.$s);
	}
}

class cUiWikiLink extends cHtmlHyperlink
{
	function __construct($title, $zCriteria = null, $argv = array()){
		if(!is_array($zCriteria)){
			//
			// No criteria - parse the title to find it
			//
			
			if(strpos("\|", $title) === false){
				// Try and split by -> (custom criteria)
				$z = split('->', $title);
				
				// First is title
				$title = $z[0];
				
				if(count($z) == 1){
				 	// Just the title
				 	$zCriteria = array('title'=>$z[0]);
				}else{
				 	
					// Now split criteria by '='
					for($n = 1; $n < count($z); $n++)
						$z[$n] = split('=', $z[$n]);
					
					if(count($z[1]) == 1){
					 	// Just the title
					 	$zCriteria = array('title'=>$z[1][0]);
					}else{
					 	// Read all
					 	for($n = 1; $n < count($z); $n++)
					 		$zCriteria[$z[$n][0]] = $z[$n][1];
					}
				}
			}else{
			 	$z = split('\\|', $title, 2);
			 	
				// Title first, then title value
				$zCriteria = array('title'=>$z[0]);
				$title = $z[1];
			}
		}
	
		//
		// Run the query
		//
		
		$q = new cDycmsQuery();
		
		if(isset($argv['clsid'])) {
			$q->SetClass($argv['clsid']);
		}
		$q->AddArray($zCriteria);
		
		$n = $q->Execute();
		
		if($n == 0){
			if(isset($argv['createCls'])){
				parent::__construct(dyEditorUrl($argv['createCls']).'&$wikiCriteria='
					. urlencode(serialize($zCriteria)), $title, array(
					'class' => 'wikiLink'
				));
			}else{
				parent::__construct('#', $title, array(
					'class' => 'brokenLink'
				));
			};
		}else if($n == 1){
		 	parent::__construct(dyViewerUrl($q->GetClsId(), $q->GetId()), $title, array(
				'class' => 'wikiLink'
			));
		}else{
		 	parent::__construct(dyBrowserUrl($zCriteria), $title, array(
				'class' => 'wikiBrowseLink'
			));
		};
	}
}

?>